<template>
  <div class="goods-sku" v-if="specs">
    <!--一个规格-->
    <dl v-for="spec in specs" :key="spec.name">
      <!--规格名字-->
      <dt>{{ spec.name }}</dt>
      <!-- 规格选项-->
      <dd>
        <template v-for="value in spec.values" :key="value.name">
          <img
            :class="{ selected: value.selected, disabled: value.disabled }"
            alt=""
            v-if="value.picture"
            :src="value.picture"
            @click="updateSelected(spec, value)"
          />
          <span
            @click="updateSelected(spec, value)"
            :class="{ selected: value.selected, disabled: value.disabled }"
            v-else
            >{{ value.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>

<script>
import powerSet from "@/vendor/powerSet";

export default {
  name: "GoodsSku",
  props: {
    specs: {
      type: Array,
      default: () => [],
    },
    skus: {
      type: Object,
      default: () => {},
    },
    skuId: {
      type: String,
      default: "",
    },
  },
  setup(props, { emit }) {
    const pathMap = createPathMap(props.skus);
    const updateSelected = (spec, value) => {
      if (value.disabled) return;

      // 如果当前规格已经选中 再点击时 取消选中
      if (value.selected) {
        // 取消选中
        value.selected = false;
      } else {
        // 先将所有的取消选中 再选中当前
        spec.values.forEach((item) => {
          item.selected = false;
        });
        // 选中当前
        value.selected = true;
      }
      // 每次取消选中或选中规格 更新禁用状态
      updateDisabled(props.specs, pathMap);
      // 如果数据完整 把数据传到父组件
      sendDataToParent(props.specs, pathMap, props.skus, emit);
    };

    updateDisabled(props.specs, pathMap);

    // 设置规格默认选中
    setDefaultSelected(props.skuId, props.skus, props.specs);
    // 如果数据完整 把数据传到父组件
    sendDataToParent(props.specs, pathMap, props.skus, emit);
    return {
      updateSelected,
      pathMap,
    };
  },
};

// 创建规格查询对象的方法
function createPathMap(skus) {
  // 规格查询对象
  const pathMap = {};
  // 遍历所有可以组合的规格组合
  skus.forEach((sku) => {
    // 判断是否有货
    if (sku.inventory > 0) {
      // 获取当前可组合的规格的名称数组
      let names = sku.specs.map((item) => item.valueName);
      // 完整的规格数量
      const max = names.length;
      // 获取当前可组合的规格名称的子集
      const sets = powerSet(names).filter((item) => item.length > 0);
      // 遍历sets这个二维数组 得到一维数组
      sets.forEach((set) => {
        // 拼接组合
        const key = set.join("_");
        if (!(key in pathMap)) {
          if (set.length === max) {
            // 完整的规格
            pathMap[key] = sku.id;
          } else {
            // 不完整的规格
            pathMap[key] = null;
          }
        }
      });
    }
  });

  // 返回规格查询对象
  return pathMap;
}

// 更新规格的禁用状态
function updateDisabled(specs, pathMap) {
  // 遍历规格数组
  specs.forEach((item, index) => {
    const selected = getSelected(specs);

    item.values.forEach((value) => {
      // 判断当前遍历到的规格是否存在 存在则不处理
      if (value.selected) return;
      // 将遍历到的规则按顺序储存到selected中
      selected[index] = value.name;
      // 将用户规格选择的名字和当前遍历到的规格名字进行组合
      const key = selected
        .filter((item) => {
          return item;
        })
        .join("_");
      // 在规格查询对象中 是否有当前规格 如果没有
      value.disabled = !(key in pathMap);
    });
  });
}

// 获取用户选择的规格
function getSelected(specs) {
  // 储存选择的规格
  const result = [];
  // 遍历在页面中渲染的供用户选择的规格数据
  specs.forEach((item, index) => {
    // 在当前遍历饿具体的规格数组中查找用户选中的规格
    const selected = item.values.find((v) => v.selected);
    // 如果当前规格对象 selected为true 为选中
    if (selected) {
      // 则添加到结果数组
      result[index] = selected.name;
    } else {
      // 插入undefined保障顺序
      result[index] = undefined;
    }
  });
  return result;
}

// 设置规格选项的默认选中效果
function setDefaultSelected(skuId, skus, specs) {
  console.log(specs);
  // 如果不存在 结束默认选中
  if (!skuId) return;
  // 从所有可组合的规格集合数组中 查找当前要默认选中的规格对象
  const target = skus.find((item) => item.id === skuId);
  // 将要选中的规格的名字储存到数据组中
  const names = target.specs.map((item) => item.valueName);
  // 遍历页面中渲染的供用户选择的规格数据
  specs.forEach((item) => {
    // 循环specs中的每个选项 中的values数组
    item.values.forEach((value) => {
      // 判断当前的遍历的规格名字 是否包含在names中 如果包含 则选中
      if (names.includes(value.name)) {
        value.selected = true;
      }
    });
  });
}

// 将用户选择的规格数据传递到父组件 以备使用
function sendDataToParent(specs, pathMap, skus, emit) {
  // 1.获取用户选择的规格 筛选选中后的 而不是undefined的数据
  const selected = getSelected(specs).filter((item) => item);
  // ['蓝色', '中国', '10cm']
  // 2.判断用户选择的规格是否是完整的 如果是完整的才需要将数据传递到组件
  if (selected.length === specs.length) {
    // 3.获取选中的规格id
    const skuId = pathMap[selected.join("_")];
    // 4.根据skuId 在所有可组合的规格集合中查找规格对象
    const target = skus.find((item) => item.id === skuId);
    console.log(target);
    // 5.将选中的规格数据 传递到父组件中
    emit("onSpecChanged", {
      // 商品的规格Id 商品加入购物车时使用
      skuId,
      // 商品现价
      price: target.price,
      // 商品原价
      oldPrice: target.oldPrice,
      // 商品库存(无法选择超过库存数量的商品)
      inventory: target.inventory,
      // 用户选择的规格名称字符串
      attrsText: target.specs
        .map((spes) => `${spes.name}:${spes.valueName}`)
        .join(" "),
    });
  } else {
    // 当用户选择的不是一个完整规格的时候 告诉父组件 清除skuid
    emit("onSpecHalfChanged");
  }
}
</script>

<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  margin-bottom: 10px;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 5px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin ();
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin ();
      }
    }
  }
}
</style>
